# Android_CheckToolPage.py  →  浅色主题版
import logging
import os
import subprocess
import threading
import time
import tkinter as tk
import re
import platform
from tkinter import ttk, filedialog, messagebox

# 不再使用深色主题
# import sv_ttk
from checktools.android_check import (
    get_environment_tools,
    check_command,
    ENV_CONFIG
)
from packingtools.android_packing import is_valid_project_root, run_packing_process, install_apk
from utils.usbWatcher import UsbWatcher

# ---------- 统一浅色变量 ----------
BG_LIGHT   = "#ffffff"
SIDEBAR_BG = "#f5f5f5"
ACCENT     = "#2563eb"
TEXT_DARK  = "#1e293b"
TEXT_MUTED = "#64748b"
BORDER     = "#e2e8f0"


class Android_CheckToolPage(tk.Frame):
    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # 状态变量
        self.project_root = tk.StringVar()
        self.sdk_path = tk.StringVar()
        self.detection_in_progress = False
        self.tool_paths = {}

        # 字体配置（与 iOS 统一）
        self.title_font   = ("Segoe UI", 14, "bold")
        self.subtitle_font = ("Segoe UI", 10)
        self.tree_font     = ("Segoe UI", 9)

        # 初始化 UI
        self._setup_widgets()
        self.after(500, self.check_toolchain)
        self.usb_watcher = UsbWatcher(callback=self._fill_adb_tree)
        self.usb_watcher.start()
        # 添加销毁标志
        self.is_destroyed = False

        # 绑定销毁事件
        self.bind("<Destroy>", self.on_destroy)

    def on_destroy(self, event):
        """窗口销毁时调用"""
        if event.widget == self:  # 确保是自身被销毁
            self.is_destroyed = True
            # 取消所有待处理的after事件
            for after_id in getattr(self, '_after_ids', []):
                self.after_cancel(after_id)
            # 停止所有后台线程
            self.stop_background_tasks()

    def stop_background_tasks(self):
        """停止所有后台任务"""
        # 停止检测线程
        if hasattr(self, 'detection_thread') and self.detection_thread.is_alive():
            # 设置停止标志（如果线程是循环的）
            if hasattr(self, 'stop_detection_flag'):
                self.stop_detection_flag = True
            # 等待线程结束（最多1秒）
            self.detection_thread.join(timeout=1.0)

        # 停止ADB刷新线程
        if hasattr(self, 'adb_refresh_thread') and self.adb_refresh_thread.is_alive():
            if hasattr(self, 'stop_adb_refresh_flag'):
                self.stop_adb_refresh_flag = True
            self.adb_refresh_thread.join(timeout=1.0)

    def safe_after(self, delay_ms, func, *args):
        """安全执行after方法"""
        if self.is_destroyed or not self.winfo_exists():
            return None

        # 记录after ID以便后续取消
        after_id = self.after(delay_ms, self._safe_call, func, *args)
        if not hasattr(self, '_after_ids'):
            self._after_ids = set()
        self._after_ids.add(after_id)
        return after_id

    def _safe_call(self, func, *args):
        """安全调用函数"""
        if self.is_destroyed or not self.winfo_exists():
            return
        try:
            func(*args)
        except tk.TclError as e:
            if "invalid command name" not in str(e):
                logging.error(f"安全调用失败: {e}")
        finally:
            # 从记录中移除after ID
            if hasattr(self, '_after_ids') and self.after_id in self._after_ids:
                self._after_ids.remove(self.after_id)

    # ------------------------------------------------------------------
    # 界面构建
    # ------------------------------------------------------------------
    def _setup_widgets(self):
        """初始化 UI 组件"""
        # 不再调用 sv_ttk.use_dark_theme()

        main = ttk.Frame(self)
        main.pack(fill=tk.BOTH, expand=True)

        # 头部标题
        header = ttk.Frame(main)
        header.pack(fill=tk.X, pady=(0, 10))
        ttk.Label(
            header,
            text="Android 开发环境检测 & 打包工具",
            font=self.title_font,
            foreground=ACCENT
        ).pack(side=tk.LEFT)
        self.time_lbl = ttk.Label(header, text="", font=self.subtitle_font,
                                  foreground=TEXT_MUTED)
        self.time_lbl.pack(side=tk.RIGHT)

        # 项目配置区
        path_frm = ttk.LabelFrame(main, text="项目配置", padding=10)
        path_frm.pack(fill=tk.X, pady=(0, 10))
        ttk.Label(path_frm, text="项目根目录：", foreground=TEXT_DARK) \
            .grid(row=0, column=0, sticky=tk.W, pady=5)
        ttk.Entry(path_frm, textvariable=self.project_root, width=50) \
            .grid(row=0, column=1, sticky=tk.EW, padx=5, pady=5)
        ttk.Button(path_frm, text="浏览…", command=self.browse_project_root) \
            .grid(row=0, column=2, padx=(5, 0), pady=5)
        ttk.Button(path_frm, text="编译打包", style="Accent.TButton",
                   command=self.build_project) \
            .grid(row=0, column=3, padx=(10, 0), pady=5)
        path_frm.columnconfigure(1, weight=1)

        # 标签页容器
        nb = ttk.Notebook(main)
        nb.pack(fill=tk.BOTH, expand=True)

        # 1. 环境检测
        env_tab = ttk.Frame(nb)
        nb.add(env_tab, text="环境检测")
        self.env_tree = ttk.Treeview(env_tab, show="tree", selectmode="none")
        self.env_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        env_scroll = ttk.Scrollbar(env_tab, orient="vertical",
                                   command=self.env_tree.yview)
        env_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.env_tree.configure(yscrollcommand=env_scroll.set)

        # 2. ADB 设备
        adb_tab = ttk.Frame(nb)
        nb.add(adb_tab, text="ADB 设备")
        self.adb_tree = ttk.Treeview(adb_tab, show="tree", selectmode="none")
        self.adb_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        adb_scroll = ttk.Scrollbar(adb_tab, orient="vertical",
                                   command=self.adb_tree.yview)
        adb_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.adb_tree.configure(yscrollcommand=adb_scroll.set)

        # 3. 项目信息
        project_tab = ttk.Frame(nb)
        nb.add(project_tab, text="项目信息")
        self.project_tree = ttk.Treeview(project_tab, show="tree", selectmode="none")
        self.project_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # 样式统一
        style = ttk.Style()
        style.configure("Accent.TButton",
                        font=("Segoe UI", 10, "bold"),
                        foreground=TEXT_DARK)

        # 标签颜色（浅色）
        for tag, color in {
            "error": "#d93025",
            "ok": "#137333",
            "warning": "#ea4335",
            "version": TEXT_MUTED,
            "path": ACCENT,
            "requirement": "#ea4335"
        }.items():
            self.env_tree.tag_configure(tag, foreground=color)
            self.adb_tree.tag_configure(tag, foreground=color)
            self.project_tree.tag_configure(tag, foreground=color)

    # ------------------------------------------------------------------
    # 原有功能方法保持不变，仅把深色 UI 调用全部替换为浅色
    # ------------------------------------------------------------------
    def browse_project_root(self):
        """浏览选择项目根目录"""
        path = filedialog.askdirectory(title="请选择 Android 项目根目录")
        if path:
            self.project_root.set(path)
            self._check_project_info(path)

    def check_toolchain(self):
        """触发环境检测（线程安全）"""
        if self.detection_in_progress:
            messagebox.showinfo("提示", "检测正在进行中，请稍候...")
            return
        self.detection_in_progress = True
        self.time_lbl.config(text="检测中...")
        threading.Thread(target=self._async_check, daemon=True).start()

    def _async_check(self):
        """异步执行环境检测（安全版本）"""
        try:
            # 检查窗口是否已被销毁
            if hasattr(self, 'is_destroyed') and self.is_destroyed:
                return

            # 安全执行环境检测
            if self.winfo_exists():
                self._fill_env_tree()
                self._fill_adb_tree()

            # 安全检查项目路径
            project_path = self.project_root.get().strip()
            if project_path and os.path.exists(project_path) and self.winfo_exists():
                self._check_project_info(project_path)

        except Exception as e:
            logging.error(f"环境检测过程中出错: {e}")

        finally:
            # 安全更新时间标签
            if hasattr(self, 'time_lbl') and self.time_lbl.winfo_exists():
                try:
                    self.after(0, lambda:
                    self.time_lbl.config(
                        text=f"最后检测时间: {time.strftime('%Y-%m-%d %H:%M:%S')}"
                    ) if hasattr(self, 'time_lbl') and self.time_lbl.winfo_exists() else None
                               )
                except tk.TclError:
                    pass  # 忽略控件已销毁的错误

            # 更新状态标志
            self.detection_in_progress = False

    def _fill_env_tree(self):
        """填充环境检测结果树（逻辑不变）"""
        self.env_tree.delete(*self.env_tree.get_children())
        root = self.env_tree.insert("", tk.END, text="环境检测结果", open=True)
        tools = get_environment_tools()

        for tool_name, check_func, description in tools:
            node = self.env_tree.insert(root, tk.END, text=tool_name)
            success, version, path, msg = check_command(
                tool_name, check_func, description)
            self.after(0, lambda n=node, s=success, v=version, p=path,
                       m=msg, d=description:
                       self._update_env_node(n, s, v, p, m, d))

    def _update_env_node(self, node, success, version, path, msg, description):
        """更新单个节点（安全版本）"""
        # 首先检查控件是否存在
        if not hasattr(self, 'env_tree') or not self.env_tree.winfo_exists():
            return

        try:
            # 检查节点是否仍然存在
            if not self.env_tree.exists(node):
                return

            # 清空节点原有内容
            for child in self.env_tree.get_children(node):
                self.env_tree.delete(child)

            # 添加新内容
            if success:
                self.env_tree.insert(node, tk.END, text="✅ 检测通过", tags=("ok",))
                self.env_tree.insert(node, tk.END, text=f"版本: {version}", tags=("version",))
                self.env_tree.insert(node, tk.END, text=f"路径: {path}", tags=("path",))
            else:
                self.env_tree.insert(node, tk.END, text=f"❌ 检测失败: {msg}", tags=("error",))
                self.env_tree.insert(node, tk.END, text=f"要求: {description}", tags=("requirement",))

                # 添加下载链接（如果存在）
                for tool in ENV_CONFIG["required"]:
                    if ENV_CONFIG["required"][tool]["description"] == description:
                        if "download" in ENV_CONFIG["required"][tool]:
                            self.env_tree.insert(
                                node, tk.END,
                                text=f"下载: {ENV_CONFIG['required'][tool]['download']}",
                                tags=("path",)
                            )
                        break
        except tk.TclError as e:
            # 捕获并忽略控件已销毁的错误
            if "invalid command name" in str(e):
                return
            # 记录其他错误
            logging.error(f"更新环境节点时出错: {e}")

    def _fill_adb_tree(self):
        """填充 ADB 设备列表（线程安全）"""
        # 使用after方法确保在主线程执行
        self.after(0, self._fill_adb_tree_safe)

    def _fill_adb_tree_safe(self):
        """真正执行刷新的逻辑（安全版本）"""
        # 首先检查控件是否存在
        if not hasattr(self, 'adb_tree') or not self.adb_tree.winfo_exists():
            return

        try:
            # 安全清空树视图
            try:
                children = self.adb_tree.get_children()
                if children:
                    self.adb_tree.delete(*children)
            except tk.TclError as e:
                if "invalid command name" in str(e):
                    return  # 控件已被销毁
                raise  # 重新抛出其他异常

            # 执行ADB命令
            adb_cmd = "adb.exe" if platform.system() == "Windows" else "adb"
            result = subprocess.run(
                [adb_cmd, "devices"],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True
            )

            # 检查控件是否仍然存在
            if not self.adb_tree.winfo_exists():
                return

            # 处理命令结果
            if result.returncode != 0:
                # 安全插入错误节点
                if self.adb_tree.winfo_exists():
                    self._insert_node(
                        self.adb_tree, "", f"ADB 执行失败: {result.stderr.strip()}", "error")
                return

            # 解析设备列表
            devices = [line.strip() for line in result.stdout.splitlines()
                       if line.strip() and "List of devices" not in line]

            if not devices:
                # 安全插入警告节点
                if self.adb_tree.winfo_exists():
                    self._insert_node(self.adb_tree, "", "未检测到连接的设备", "warning")
                return

            # 添加设备分组节点
            if not self.adb_tree.winfo_exists():
                return

            device_group = self.adb_tree.insert(
                "", tk.END, text=f"已连接设备 ({len(devices)})", open=True)

            # 添加每个设备
            for idx, dev in enumerate(devices):
                # 每次操作前检查控件有效性
                if not self.adb_tree.winfo_exists():
                    return

                # 提取设备ID和状态
                parts = dev.split()
                device_id = parts[0]
                status = "在线" if len(parts) > 1 and parts[1] == "device" else "离线"
                tag = "ok" if status == "在线" else "offline"

                # 安全插入设备节点
                if self.adb_tree.winfo_exists():
                    self._insert_node(
                        self.adb_tree, device_group,
                        f"设备 {idx + 1}: {device_id} ({status})", tag
                    )

            # 绑定右键事件 - 确保控件存在
            if self.adb_tree.winfo_exists():
                self.adb_tree.bind("<Button-3>", self._show_adb_context_menu)

        except Exception as e:
            # 插入错误节点前检查控件是否仍然有效
            if self.adb_tree.winfo_exists():
                self._insert_node(self.adb_tree, "", f"设备扫描异常: {str(e)}", "error")
            # 记录异常
            logging.error(f"刷新ADB设备列表时出错: {e}")

    def _show_adb_context_menu(self, event):
        """显示 ADB 设备的右键菜单"""
        item_id = self.adb_tree.identify_row(event.y)
        if not item_id:
            return

        tags = self.adb_tree.item(item_id, "tags")
        # 只为状态为 "ok" (在线) 的设备显示菜单
        if tags and tags[0] == "ok":
            device_info = self.adb_tree.item(item_id, "text")
            device_sn = device_info.split(":")[1].strip().split()[0]  # 提取设备序列号
            self.adb_tree.selection_set(item_id)

            menu = tk.Menu(self, tearoff=0)
            menu.add_command(
                label=f"向 {device_sn[:12]}... 安装 APK",
                command=lambda sn=device_sn: self._trigger_apk_install(sn)
            )
            menu.post(event.x_root, event.y_root)

    def _trigger_apk_install(self, device_sn):
        """触发 APK 安装流程"""
        apk_path = filedialog.askopenfilename(
            title=f"为设备 {device_sn} 选择 APK 文件",
            filetypes=[("APK files", "*.apk")]
        )
        if not apk_path:
            return

        result = install_apk(device_sn, apk_path)
        self.after(0, self._show_install_result, result)

    def _show_install_result(self, result):
        """显示安装结果"""
        if result["success"]:
            messagebox.showinfo("安装结果", result["output"])
        else:
            messagebox.showerror("安装结果", result["output"])

    def on_usb_change(self):
        # 在这里处理 USB 状态变化的逻辑
        print("Android_CheckToolPage: USB 状态发生变化")
        self._fill_adb_tree_safe()  # 刷新设备列表

    def _check_project_info(self, project_path):
        """检测项目基本信息（逻辑不变）"""
        self.project_tree.delete(*self.project_tree.get_children())
        if not is_valid_project_root(project_path):
            self._insert_node(
                self.project_tree, "",
                "项目结构不完整（缺少 build.gradle 或 AndroidManifest.xml）", "error")
            return

        self._insert_node(self.project_tree, "", f"项目路径: {project_path}",
                          "path")
        app_gradle = os.path.join(project_path, "app", "build.gradle")
        if os.path.exists(app_gradle):
            with open(app_gradle, "r", encoding="utf-8", errors="ignore") as f:
                content = f.read()
                app_id_match = re.search(r"applicationId\s+['\"](.*?)['\"]", content)
                if app_id_match:
                    self._insert_node(self.project_tree, "",
                                      f"应用ID: {app_id_match.group(1)}", "ok")
                sdk_match = re.search(r"compileSdkVersion\s+(\d+)", content)
                if sdk_match:
                    self._insert_node(self.project_tree, "",
                                      f"编译SDK版本: {sdk_match.group(1)}", "ok")

    def build_project(self):
        """编译打包项目（逻辑不变，仅弹窗样式同步浅色）"""
        path = self.project_root.get().strip()
        if not path or not os.path.exists(path):
            messagebox.showwarning("路径无效", "请先选择有效的安卓项目路径！")
            return
        if not is_valid_project_root(path):
            messagebox.showwarning("项目无效", "所选路径不是有效的 Android 项目")
            return

        # 打包进度窗口（浅色）
        self.win = tk.Toplevel(self)
        self.win.title("Android 项目打包中…")
        self.win.geometry("600x400")
        self.win.grab_set()
        self.win.resizable(True, True)

        ttk.Label(self.win, text="正在执行打包流程…").pack(pady=10)
        self.pb = ttk.Progressbar(self.win, mode="indeterminate")
        self.pb.pack(fill=tk.X, padx=20, pady=5)
        self.pb.start()

        ttk.Label(self.win, text="打包日志:").pack(anchor=tk.W, padx=20, pady=5)
        self.log = tk.Text(self.win, height=15, state="disabled",
                           bg=BG_LIGHT, fg=TEXT_DARK)
        self.log.pack(fill=tk.BOTH, expand=True, padx=20, pady=5)
        log_scroll = ttk.Scrollbar(self.log, command=self.log.yview)
        log_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.log.configure(yscrollcommand=log_scroll.set)

        btn_frame = ttk.Frame(self.win)
        btn_frame.pack(fill=tk.X, padx=20, pady=10)
        ttk.Button(btn_frame, text="取消", command=self.win.destroy).pack(side=tk.RIGHT)

        threading.Thread(target=self._run_packing, args=(path,), daemon=True).start()

    def _run_packing(self, path):
        """后台打包逻辑（不变）"""
        result = run_packing_process(path, self._log_append)
        self.after(0, self.pb.stop)

        if result["errors"]:
            self._log_append(f"\n❌ 打包失败: {', '.join(result['errors'])}")
            self.after(1000, lambda: messagebox.showerror(
                "打包失败", "\n".join(result["errors"])))
        elif result["apk_files"]:
            self._log_append("\n✅ 打包成功！生成 APK:")
            for apk in result["apk_files"]:
                self._log_append(f"  - {apk}")
            self.after(1000, lambda: messagebox.showinfo(
                "打包成功", f"APK 文件已生成:\n{result['apk_files'][0]}"))
        else:
            self._log_append("\n⚠️ 打包完成，但未找到 APK 文件")
            self.after(1000, lambda: messagebox.showwarning(
                "警告", "未检测到生成的 APK 文件"))

    def _log_append(self, txt):
        """追加日志"""
        self.log.configure(state="normal")
        self.log.insert(tk.END, txt + "\n")
        self.log.configure(state="disabled")
        self.log.see(tk.END)

    def fix_environment(self):
        """环境修复建议（弹窗样式已默认浅色，无需修改）"""
        messagebox.showinfo(
            "环境修复",
            "请根据以下建议修复环境问题：\n"
            "1. Java: 安装 JDK 11+ 并配置 JAVA_HOME\n"
            "2. Android SDK: 设置 ANDROID_HOME 环境变量\n"
            "3. ADB: 确保 SDK 的 platform-tools 目录在 PATH 中\n"
            "4. Gradle: 安装并配置环境变量或通过项目 gradlew 自动配置"
        )

    def _insert_node(self, tree, parent, text, tag=None, open=False):
        """插入树节点"""
        return tree.insert(parent, tk.END, text=text, tags=(tag or ""), open=open)

    def destroy(self):
        """销毁时关闭 USB 监听"""
        self.usb_watcher.stop()
        super().destroy()